/*
 * ULSF Sagittarius IDA
 * Copyright (C) 2012, John Pritchard, all rights reserved.
 * 
 * This program is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see
 * <http://www.gnu.org/licenses/>.
 */
package sagittarius.design;

import sagittarius.Field;
import sagittarius.Visualization;

import sagittarius.airfoil.Airfoil;

import lxl.Map;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * The {@link Volume} constraint operator is a function of user input
 * {@link sagittarius.Visualization Visualization} parameters that
 * produces objective area values for the two dimensional {@link
 * Section} - {@link Model} operators.
 * 
 * <h3>Configuration</h3>
 * 
 * The {@link Volume} operator is constructed by the visualization
 * operator {@link Body} via the system property
 * <code>"sagittarius.design.Volume"</code>.
 * 
 * <h3>Cache</h3>
 * 
 * <p> Each volume is a {@link Section} cache so that no section is
 * computed twice. </p>
 * 
 * @see SearsHaackHyperbolicWing
 */
public abstract class Volume
    extends Map<Section.Key,Section>
    implements fv3.math.Notation
{
    /**
     * System property "sagittarius.design.Volume" is employed via
     * {@link #Constructor()} by {@link Body} as a classname for a
     * class with a public constructor with one parameter {@link
     * Visualization}.
     * 
     * @see java.lang.Class#forName(String)
     */
    public final static String Configuration(){

        String classname = System.getProperty("sagittarius.design.Volume");
        if (null == classname)
            classname = "sagittarius.design.SearsHaackHyperbolicWing";

        return classname;
    }
    public final static void Configuration(String classname){
        try {
            Class<?> oclas = (Class<?>)Class.forName(classname);
            if (Volume.class.isAssignableFrom(oclas)){

                System.setProperty("sagittarius.design.Volume",classname);
            }
            else
                throw new IllegalArgumentException(classname);
        }
        catch (ClassNotFoundException exc){
            throw new IllegalArgumentException(classname,exc);
        }
    }
    /**
     * Dynamic configuration function used by {@link Body} to
     * construct volume.
     */
    public final static Volume Constructor(Visualization data){

        return Volume.Constructor(Volume.Configuration(),data);
    }
    /**
     * Dynamic binding requires public constructor having parameter {@link Visualization}
     */
    public final static Class ConstructorParameter = Visualization.class;

    public final static Volume Constructor(String classname, Visualization data){
        if (null != classname && null != data){
            try {
                Class<?> oclas = (Class<?>)Class.forName(classname);
                if (Volume.class.isAssignableFrom(oclas)){
                    Class<Volume> clas = (Class<Volume>)oclas;
                    Constructor<Volume> ctor = clas.getConstructor(ConstructorParameter);
                    return ctor.newInstance(data);
                }
                else
                    throw new IllegalArgumentException(classname);
            }
            catch (NoSuchMethodException exc){
                throw new IllegalStateException(classname,exc);
            }
            catch (ClassNotFoundException exc){
                throw new IllegalStateException(classname,exc);
            }
            catch (SecurityException exc){
                throw new IllegalStateException(classname,exc);
            }
            catch (InstantiationException exc){
                throw new IllegalStateException(classname,exc);
            }
            catch (IllegalAccessException exc){
                throw new IllegalStateException(classname,exc);
            }
            catch (InvocationTargetException exc){
                Throwable cause = exc.getCause();
                if (cause instanceof ThreadDeath)
                    throw (ThreadDeath)cause;
                else
                    throw new IllegalStateException(classname,cause);
            }
        }
        else
            throw new IllegalArgumentException(classname);
    }



    public final double radius, length, volume, center;

    public final double span, span2;
    public final double alpha, beta, gamma, delta, epsilon;
    public final double zeta, eta, theta, iota, kappa;

    public final String string;

    private Airfoil airfoilModel;

    private float dl;


    protected Volume(Visualization data, double radius, double length, double volume){
        super();
        this.radius = radius;
        this.length = length;
        this.center = (length/2.0);
        this.volume = volume;
        this.string = String.format("%g %g %g",this.radius,this.length,this.volume);
        this.span = data.getSpan();
        this.span2 = (this.span/2.0f);
        this.alpha = data.getAlpha();
        this.beta = data.getBeta();
        this.gamma = data.getGamma();
        this.delta = data.getDelta();
        this.epsilon = data.getEpsilon();
        this.zeta = data.getZeta();
        this.eta = data.getEta();
        this.theta = data.getTheta();
        this.iota = data.getIota();
        this.kappa = data.getKappa();
    }


    public final double getSpanRatio(){
        return (this.span / Field.Span.max);
    }
    public final double getRadiusRatio(){
        return (this.radius / Field.Radius.max);
    }
    /**
     * Span clamp
     *
     * @param y Span coordinate
     *
     * @return Minimum of span coordinate and maximum span
     */
    public final double span(double y){
        if (0.0 > y){

            return -(Math.min(Math.abs(y),this.span2));
        }
        else
            return Math.min(y,this.span2);
    }
    public final double spanRatio(double y){

        return (Math.abs(y)/this.span2);
    }
    /**
     * Airfoil initialization called from {@link Body}
     */
    public final void airfoils(Visualization viz, int np){
        this.airfoilModel = Airfoil.Constructor(viz);
        this.airfoilModel.init(np);
        this.airfoilModel.generate();
    }

    public final Float airfoilZ(double station, double x, double y, double span, boolean top){

        final double sr = Math.abs(y)/span;

        final double cs = station * sr;

        final float chord = (float)(this.length-(cs*2));

        Airfoil foil = this.airfoilModel.clone();

        final float cx = (float)(Math.abs(x) * sr);

        foil.transform(cx,chord);

        float[] fc = foil.getSectionVertices3At( (float)x);

        if (null == fc)
            return null;

        else {

            switch(fc.length){

            case 3:
                return fc[Z];

            case 6:
                if (top)
                    return fc[3+Z];
                else
                    return fc[Z];

            default:
                throw new Error(String.valueOf(fc.length));
            }
        }
    }

    /**
     * @param x Longitudinal coordinate over zero to length
     * 
     * @return A number between zero and one for longitude from head
     * to tail
     */
    public final double fraction(double x){
        return (x/this.length);
    }
    /**
     * @param x Longitudinal coordinate over zero to length
     * 
     * @return A number between zero and one for central longitude,
     * where 1.0 is the center and 0.0 is the head and tail
     */
    public final double uniform(double x){
        if (x < this.center)
            return (x/center);
        else {
            return ((center-(x-center))/center);
        }
    }
    /**
     * @param station Volume station between 0.0 and length, inclusive.
     * @param x Volume coordinate between 0.0 and +/-(length/2),
     * inclusive.
     * @return Two dimensional section of station
     */
    public abstract Section stationForX(double station, double x);

    /**
     * Fixed volume distribution parameter
     * @param x Volume station between 0.0 and length, inclusive.
     * @return Ratio of distance from center and magnitude of center
     */
    public final double centerOfX(double x){

        if (x < this.center)
            return (this.center - x)/this.center;
        else
            return (x - this.center)/this.center;
    }
    /**
     * @param x Longitudinal coordinate 
     * @return Ideal area of cross section 
     */
    public final double areaForX(double x){
        double rx = this.radiusForX(x);
        return (Math.PI * Math.pow(rx,2.0));
    }
    /**
     * @param x Longitudinal coordinate
     * @return Radius of circular ideal cross section 
     */
    public abstract double radiusForX(double x);

    public String toString(){
        return this.string;
    }
    public boolean equals(Volume that){
        if (null == that)
            return false;
        else {
            return (this.length == that.length && this.radius == that.radius && this.volume == that.volume);
        }
    }
}
